﻿using EnvDTE;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Axiom.CodeManifold
{
	public static class ProjectExtensions
	{
		public static IDictionary<string, FileCodeModel> GetCodeModels(this Project project)
		{
			var result = new Dictionary<string, FileCodeModel>();

			var items = project.GetProjectItems();
			foreach (var item in items.Values)
			{
				FileCodeModel model = item.FileCodeModel;
				if (model != null)
				{
					result.Add(item.Name, model);
				}
			}

			return result;
		}

		public static IDictionary<string, Project> GetSolutionProjects(this Projects projects, Dictionary<string, Project> result = null)
		{
			if (result == null)
			{
				result = new Dictionary<string, Project>();
			}

			foreach (Project project in projects)
			{
				GetSolutionProjectsRecursive(project, result);
			}

			return result;
		}

		private static void GetSolutionProjectsRecursive(Project project, Dictionary<string, Project> result)
		{
			if (project.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}") // Solution Folder - Project Item Kind
			{
				foreach (ProjectItem item in project.ProjectItems)
				{
					if (item.SubProject != null)
					{
						GetSolutionProjectsRecursive(item.SubProject, result);
					}
				}
			}
			else
			{
				result.Add(project.Name, project);
			}
		}

		public static ProjectItem GetProjectItem(this Project project, string name)
		{
			var items = project.GetProjectItems();

			ProjectItem item = null;
			if (items.ContainsKey(name))
			{
				item = items[name];
			}

			return item;
		}

		public static IDictionary<string, ProjectItem> GetProjectItems(this Project project)
		{
			return project.ProjectItems.GetProjectItems();
		}

		public static IDictionary<string, ProjectItem> GetProjectItems(this ProjectItems items, Dictionary<string, ProjectItem> result = null)
		{
			if (result == null)
			{
				result = new Dictionary<string, ProjectItem>();
			}

			foreach (ProjectItem item in items)
			{
				GetProjectItemsRecoursive(item, result);
			}

			return result;
		}

		private static void GetProjectItemsRecoursive(ProjectItem item, Dictionary<string, ProjectItem> result)
		{
			if (item.ProjectItems != null && item.ProjectItems.Count > 0)
			{
				GetProjectItems(item.ProjectItems, result);
			}
			else
			{
				result.Add(item.Name, item);
			}
		}

		public static T GetCodeElement<T>(this Project project, string name) where T : class
		{
			T result = null;

			var models = project.GetCodeModels();
			var any = models.Any(a => a.Key.Contains(name));
			var list = models.Where(a => a.Key.Contains(name)).ToList();

			foreach (var model in list)
			{
				result = model.Value.GetEntity<T>(name);
				if (result != null)
				{
					break;
				}
			}

			if (result == null)
			{
				Parallel.ForEach(models, a =>
				{
					if (result != null)
					{
						return;
					}

					var temp = a.Value.GetEntity<T>(name);

					if (temp != null)
					{
						result = temp;
					}

				});
			}


			return result;
		}

		public static IDictionary<string, T> GetAllCodeElements<T>(this Project project) where T : class
		{
			IDictionary<string, T> result = new Dictionary<string, T>();

			var models = project.GetCodeModels();

			Parallel.ForEach(models, a =>
			{
				var temp = a.Value.GetEntities<T>();

				foreach (dynamic item in temp)
				{
					result.Add(item.Name, item);
				}
			});

			return result;
		}

		public static IDictionary<string, T> GetImplementationsOf<T>(this Project project, string interfacename) where T : class
		{
			IDictionary<string, T> result = new Dictionary<string, T>();

			var models = project.GetCodeModels();

			CodeClass c = null;

			Parallel.ForEach(models, a =>
			{
				var temp = a.Value.GetEntities<T>();

				foreach (dynamic item in temp)
				{
					foreach(CodeInterface i in item.ImplementedInterfaces)
					{
						if (i.Name.CompareTo(interfacename) == 0)
						{
							result.Add(item.Name, item);
						}
					}					
				}
			});

			return result;
		}
	}
}
